package stagebase

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
	"wtester/pkg/parabase"
	"wtester/pkg/resultbase"
)

/**
  @author: weicai
  @since: 2024/3/11
  @desc: 执行阶段
**/

type Stage struct {
	Concurrency int                                    // 本阶段并发数
	SpawnRate   float32                                // 执行期的启动的速率
	Actuator    []*Actuator                            // 执行器
	Duration    float64                                // 执行器需要执行的总时长，如果小于等于0，则不设定执行时长
	NextStage   *Stage                                 // 下一个执行阶段
	ParamRange  map[string]*chan string                // 阶段共享的参数池，迭代型参数
	ParamRandom map[string]parabase.TaskParamInterface // 阶段共享的参数池，随机型参数

	startTime      time.Time      // 测试启动的时间
	finishSignal   *chan bool     // 结束信号
	curConcurrency *atomic.Uint32 // 当前的并发数
	isFinish       bool           // 是否结束
}

// 心跳线程，根据设定的时间来终止当前的阶段
func (s *Stage) heartbeat() {
	tk := time.NewTicker(1 * time.Second)
	for range tk.C {
		*s.finishSignal <- true
		now := time.Now()
		d := now.Sub(s.startTime).Seconds()
		if d >= s.Duration || s.isFinish {
			tk.Stop()
			close(*s.finishSignal)
			break
		}
	}
}

// 将权重转化为启动的固定线程的个数
func (s *Stage) weight2FixConcurrency() {
	for _, a := range s.Actuator {
		if a.FixConcurrency == 0 {
			if a.Weight != 0 {
				a.FixConcurrency = uint8(float32(s.Concurrency) * a.Weight / 100)
			}
		}
	}
}

// 当前阶段是由Duration控制的结束还是由Actuator自动控制结束
func (s *Stage) auto0DurationStop() string {
	hasMaster := "0"
	for _, a := range s.Actuator {
		if a.IsMaster {
			if hasMaster == "1" {
				return "2"
			}
			hasMaster = "1"
		}
	}
	return hasMaster
}

func (s *Stage) Run(results *chan *resultbase.WtesterResults) {
	s.startTime = time.Now()
	s.weight2FixConcurrency()
	// 第一阶段执行时，设置结果
	if results == nil {
		r := make(chan *resultbase.WtesterResults, 100)
		results = &r
		go resultbase.DealResults(results)
	}
	var group sync.WaitGroup
	// 判断是否存在阶段为master的线程
	hasMaster := s.auto0DurationStop()
	// 阶段终止信号接收器
	*s.finishSignal = make(chan bool, 100)
	switch hasMaster {
	case "0":
		group.Add(1)
		go func() {
			defer group.Done()
			s.heartbeat()
		}()
	case "1":
		fmt.Println("此阶段有主线程，其他测试线程将会在主线程结束后结束")
	default:
		fmt.Println("此阶段有多个主线程，无法确定结束时间，程序将会退出")
		return
	}
	s.curConcurrency = new(atomic.Uint32)
	// 记录当前的并发数
	index, length := 0, len(s.Actuator)
	tk := time.NewTicker(time.Duration(1 / s.SpawnRate * float32(time.Second)))
	for range tk.C {
		actuator := s.Actuator[index]
		s.Actuator[index].stage = s
		ac := actuator.curConcurrency
		// 如果当前actuator的并发数已经达到最大值，则切换到下一个actuator
		if ac >= actuator.FixConcurrency {
			index++
			// 如果所有的actuator都已经启动完成，则停止
			if index >= length {
				tk.Stop()
				break
			}
			actuator = s.Actuator[index]
		}
		group.Add(1)
		go func() {
			defer group.Done()
			actuator.Run(results)
		}()
		actuator.curConcurrency++
		// 记录当前并发数
		s.curConcurrency.Add(1)
		fmt.Printf("启动测试：%s；启动Actuator个数：%d\n", actuator.Name, s.curConcurrency.Load())
		if int(s.curConcurrency.Load()) >= s.Concurrency {
			tk.Stop()
			break
		}
	}
	group.Wait()
	if s.NextStage != nil {
		s.NextStage.Run(results)
	}
	fmt.Println("阶段结束")
	close(*results)
}
